package de.global42.clubapp.ejbs;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;

import de.global42.clubapp.model.Client;
import de.global42.clubapp.model.ClientMission;
import de.global42.clubapp.model.Mission;
import de.global42.clubapp.model.ValidationCode;
import de.global42.clubapp.model.Venue;
import de.global42.clubapp.util.StatusContainer;

/**
 * @author lutz
 *
 */
@Stateless
public class ClientMissionHandlerBean implements ClientMissionHandler {
	
	private EntityAccessor entityAccessor;
	
	private EntityManager entityManager;
	
	private ClientCouponHandler clientCouponHandler;
	
	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public Map<Long, Set<Mission>> getMissions(Long clientId, Date lastUpdated) {
		Map<Long, Set<Mission>> venueMissionsMap = new HashMap<Long, Set<Mission>>();
		Client client;
		try {
			client = entityAccessor.getEntity(Client.class, "identifier", clientId.toString());
			for (Venue venue : client.getVenues()) {
				if (lastUpdated == null || lastUpdated.before(venue.getMissionsUpdatedOn())) {
					venueMissionsMap.put(venue.getId(), venue.getMissions());
				}
			}		
		} catch (NoResultException e) {
			// TODO Auto-generated catch block
		} catch (Exception e) {
			// TODO Auto-generated catch block
		}
		return venueMissionsMap;
	}

	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public Map<Long, Object> getClientMissions(Long clientId) {
		Map<Long, Object> missionsMap = new HashMap<Long, Object>();
		Client callingClient;
		try {
			callingClient = entityAccessor.getEntity(Client.class, "identifier", clientId.toString());
			Mission parentMission;
			for (ClientMission clientMission : callingClient.getClientMissions()) {
				parentMission = clientMission.getMission();
				missionsMap.put(parentMission.getId(), new StatusContainer(clientMission, parentMission)); 
			}
		} catch (NoResultException e) {
			// TODO Auto-generated catch block
		} catch (Exception e) {
			// TODO Auto-generated catch block
		}
		return missionsMap;
	}
	
	//TODO create interceptor for flushing after the execution of the method
	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public String enterValidationCode(Long clientId, String validationCode, Long venueId) {
		String verificationSequence = new String();
		try {
			Client callingClient = entityAccessor.getEntity(Client.class, "identifier", clientId.toString());
			ValidationCode  code = getValidationCode(venueId, validationCode);//entityAccessor.getEntity(ValidationCode.class, "code", validationCode);
			if (code.isStillValid()) {
				ClientMission clientMission = getClientMission(callingClient, code.getMission());
				clientMission.getMissionStatus().progress();
				code.devaluate();
				clientCouponHandler.checkForCouponsAsync(clientId);
				verificationSequence = generateVerificationSequence(clientId,validationCode);
			}
		} catch (NoResultException e) {
			// TODO Auto-generated catch block
		} catch (Exception e) {
			// TODO Auto-generated catch block
		}
		
		return verificationSequence;
	}
	
	private ValidationCode getValidationCode(Long venueId, String validationCode) throws NoResultException, Exception {
		try {//TODO: verify JPQL syntax
			String qlString = "SELECT DISTINCT vc FROM ValidationCode vc WHERE vc.code = :code AND vc.venue = :venue";
			TypedQuery<ValidationCode> query = entityManager.createQuery(qlString, ValidationCode.class);
			query.setParameter("code", validationCode);
			query.setParameter("venue", venueId);					
			return query.getSingleResult();
		} catch (NoResultException e) {
			throw new NoResultException();
		}
		catch (Exception e) {
			throw new Exception(e);
		}
	}

	//TODO: review (just POC)
	private String generateVerificationSequence(Long clientId, String validationCode) {
		return String.valueOf(clientId.toString()+validationCode.toString().hashCode());
	}

	private ClientMission getClientMission(Client client, Mission mission) {
		ClientMission clientMission = new ClientMission();
		try {
			clientMission = fetchClientMission(client, mission);
		} catch (NoResultException e) {
			clientMission = createNewClientMission(client, mission);
		} catch (Exception e) {
			// TODO Auto-generated catch block
		}
		return clientMission;
	}

	private ClientMission createNewClientMission(Client client, Mission mission) {
		ClientMission newClientMission = new ClientMission();
		newClientMission .setClient(client);
		newClientMission.setMission(mission);
		
		return newClientMission;
	}

	//TODO: maybe generalize fetchtClientMission(..) and move to EntityAccessor?!
	private ClientMission fetchClientMission(Client client, Mission mission) throws NoResultException, Exception {
		try {
			//TODO: verify JPQL syntax
			String qlString = "SELECT DISTINCT cm FROM ClientMission cm WHERE cm.client_id = :clientId AND cm.mission_id = :missionId";
			TypedQuery<ClientMission> query = entityManager.createQuery(qlString, ClientMission.class);
			query.setParameter("clientId", client.getId());
			query.setParameter("missionId", mission.getId());
			return query.getSingleResult();
		} catch (NoResultException e) {
			throw new NoResultException();
		}
		catch (Exception e) {
			throw new Exception(e);
		}
	}

	@EJB
	public void setEntityAccessor(EntityAccessor entityAccessor) {
		this.entityAccessor = entityAccessor;
	}

	@PersistenceContext
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
	
	@EJB
	public void setClientCouponHandler(ClientCouponHandler clientCouponHandler) {
		this.clientCouponHandler = clientCouponHandler;
	}

}
